{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cd28d0d4",
   "metadata": {},
   "source": [
    "## 动态断点\n",
    "\n",
    "目标\n",
    "断点由开发人员在图表编译期间在特定节点上设置。\n",
    "不过有时允许图表动态中断自身也是一个比较大的需求\n",
    "这是一个内部断点，可以使用 NodeInterrupt实现。\n",
    "这有几个具体的好处：\n",
    " (1)您可以有条件地执行此操作（从基于开发人员定义的逻辑的节点内部执行）。\n",
    " (2)您可以向用户传达其中断的原因（通过将您想要的任何内容传递给NodeInterrupt）。\n",
    " \n",
    "让我们创建一个图表，其中根据输入的长度抛出一个“NodeInterrupt”。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "682c69a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph langchain_openai langgraph_sdk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e6005940",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Package                  Version\n",
      "------------------------ -----------\n",
      "aiohappyeyeballs         2.6.1\n",
      "aiohttp                  3.11.18\n",
      "aiosignal                1.3.2\n",
      "annotated-types          0.6.0\n",
      "anyio                    4.6.2\n",
      "asttokens                3.0.0\n",
      "async-timeout            4.0.3\n",
      "attrs                    25.3.0\n",
      "beautifulsoup4           4.13.4\n",
      "Brotli                   1.0.9\n",
      "certifi                  2025.1.31\n",
      "charset-normalizer       3.3.2\n",
      "colorama                 0.4.6\n",
      "comm                     0.2.1\n",
      "dataclasses-json         0.6.7\n",
      "debugpy                  1.8.11\n",
      "decorator                5.1.1\n",
      "distro                   1.9.0\n",
      "exceptiongroup           1.2.0\n",
      "executing                0.8.3\n",
      "frozenlist               1.6.0\n",
      "greenlet                 3.2.0\n",
      "h11                      0.14.0\n",
      "httpcore                 1.0.2\n",
      "httpx                    0.27.0\n",
      "httpx-sse                0.4.0\n",
      "idna                     3.7\n",
      "ipykernel                6.29.5\n",
      "ipython                  8.30.0\n",
      "jedi                     0.19.2\n",
      "jiter                    0.6.1\n",
      "jsonpatch                1.33\n",
      "jsonpointer              3.0.0\n",
      "jupyter_client           8.6.3\n",
      "jupyter_core             5.7.2\n",
      "langchain                0.3.23\n",
      "langchain-community      0.3.21\n",
      "langchain-core           0.3.59\n",
      "langchain-openai         0.3.16\n",
      "langchain-text-splitters 0.3.8\n",
      "langgraph                0.3.31\n",
      "langgraph-checkpoint     2.0.24\n",
      "langgraph-prebuilt       0.1.8\n",
      "langgraph-sdk            0.1.63\n",
      "langsmith                0.3.32\n",
      "marshmallow              3.26.1\n",
      "matplotlib-inline        0.1.6\n",
      "multidict                6.4.3\n",
      "mypy-extensions          1.0.0\n",
      "nest-asyncio             1.6.0\n",
      "numpy                    2.2.5\n",
      "openai                   1.75.0\n",
      "orjson                   3.10.16\n",
      "ormsgpack                1.9.1\n",
      "packaging                24.2\n",
      "parso                    0.8.4\n",
      "pip                      25.0\n",
      "platformdirs             4.3.7\n",
      "prompt-toolkit           3.0.43\n",
      "propcache                0.3.1\n",
      "psutil                   5.9.0\n",
      "pure-eval                0.2.2\n",
      "pydantic                 2.10.3\n",
      "pydantic_core            2.27.1\n",
      "pydantic-settings        2.9.1\n",
      "Pygments                 2.19.1\n",
      "PySocks                  1.7.1\n",
      "python-dateutil          2.9.0.post0\n",
      "python-dotenv            1.1.0\n",
      "pywin32                  308\n",
      "PyYAML                   6.0.2\n",
      "pyzmq                    26.2.0\n",
      "regex                    2024.11.6\n",
      "requests                 2.32.3\n",
      "requests-toolbelt        1.0.0\n",
      "setuptools               75.8.0\n",
      "six                      1.17.0\n",
      "sniffio                  1.3.0\n",
      "soupsieve                2.7\n",
      "SQLAlchemy               2.0.40\n",
      "stack-data               0.2.0\n",
      "tavily-python            0.7.2\n",
      "tenacity                 9.1.2\n",
      "tiktoken                 0.9.0\n",
      "tornado                  6.4.2\n",
      "tqdm                     4.67.1\n",
      "traitlets                5.14.3\n",
      "typing_extensions        4.12.2\n",
      "typing-inspect           0.9.0\n",
      "typing-inspection        0.4.0\n",
      "urllib3                  2.3.0\n",
      "wcwidth                  0.2.5\n",
      "wheel                    0.45.1\n",
      "wikipedia                1.4.0\n",
      "win-inet-pton            1.1.0\n",
      "xxhash                   3.5.0\n",
      "yarl                     1.20.0\n",
      "zstandard                0.23.0\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "pip list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d8cf34a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.errors import NodeInterrupt\n",
    "from langgraph.graph import START, END, StateGraph\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "\n",
    "def step_1(state: State) -> State:\n",
    "    print(\"---第一步---\")\n",
    "    return state\n",
    "\n",
    "def step_2(state: State) -> State:\n",
    "    # 如果输入的长度超过 5 个字符，我们可以选择性地引发 NodeInterrupt\n",
    "    if len(state['input']) > 5:\n",
    "        raise NodeInterrupt(f\"收到的长度超过 5 个字符的输入： {state['input']}\")\n",
    "    \n",
    "    print(\"---Step 2---\")\n",
    "    return state\n",
    "\n",
    "def step_3(state: State) -> State:\n",
    "    print(\"---Step 3---\")\n",
    "    return state\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"step_2\", step_2)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"step_2\")\n",
    "builder.add_edge(\"step_2\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# 设置记忆\n",
    "memory = MemorySaver()\n",
    "\n",
    "# 编译图表\n",
    "graph = builder.compile(checkpointer=memory)\n",
    "\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30b4e7b5",
   "metadata": {},
   "source": [
    "让我们使用长度超过5个字符的输入来运行图表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "98df3ec3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': '你好今天天气怎么样？'}\n",
      "---第一步---\n",
      "{'input': '你好今天天气怎么样？'}\n"
     ]
    }
   ],
   "source": [
    "initial_input = {\"input\": \"你好今天天气怎么样？\"}\n",
    "thread_config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# 运行图直至第一次中断\n",
    "for event in graph.stream(initial_input, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04d0cc5d",
   "metadata": {},
   "source": [
    "如果我们此时检查图形状态，我们将设置节点以执行下一步（“step_2”）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8f0e4859",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('step_2',)\n"
     ]
    }
   ],
   "source": [
    "state = graph.get_state(thread_config)\n",
    "print(state.next)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8b55b4e",
   "metadata": {},
   "source": [
    "我们可以看到“中断”已记录到状态。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d75b4f23",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(PregelTask(id='0e88d08f-e4c8-eaa1-77ef-cd96c43dca71', name='step_2', path=('__pregel_pull', 'step_2'), error=None, interrupts=(Interrupt(value='收到的长度超过 5 个字符的输入： 你好今天天气怎么样？', resumable=False, ns=None),), state=None, result=None),)\n"
     ]
    }
   ],
   "source": [
    "print(state.tasks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32e79d13",
   "metadata": {},
   "source": [
    "我们可以尝试从断点处恢复图表。<br/>\n",
    "但是，这只是重新运行同一个节点！<br/>\n",
    "除非状态改变，否则我们将被困在这里。<br/>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "eca3c755",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': '你好今天天气怎么样？'}\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e8fcea2e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'configurable': {'thread_id': '1',\n",
       "  'checkpoint_ns': '',\n",
       "  'checkpoint_id': '1f031639-51fa-6620-8002-aa32d6a6791a'}}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.update_state(\n",
    "    thread_config,\n",
    "    {\"input\": \"你好\"},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "15ade068",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': '你好'}\n",
      "---Step 2---\n",
      "{'input': '你好'}\n",
      "---Step 3---\n",
      "{'input': '你好'}\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, thread_config, stream_mode=\"values\"):\n",
    "    print(event)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8964ec8b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10",
   "language": "python",
   "name": "python310"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
